import "reflect-metadata";
import {parseScript} from "esprima"

interface IIndexService {
  log(str: string): void;
}

interface ITypes {
  [key: string]: Symbol;
}

const Types:ITypes = {
  "indexService": Symbol.for("indexService")
};


class IndexService implements IIndexService{
  log(str: string) {
    console.log("熊", str);
  }
}

function inject(serviceIdentifier:Symbol){
  // step 1
  console.log('step1', '我进入到inject,准备对方法参数进行修饰');
  return (target: object/** 装饰所处的类*/, targetKey: string /** ←装饰所处的方法的名字*/, index: number/**←装饰的方法的第几个参数*/) => {
    console.log('step2', '进行具体的对构造函数的装饰');
    if (!targetKey/**←没有targetKey,即为构造函数*/) {
      Reflect.defineMetadata(serviceIdentifier/**←要添加的metaDataKey*/, new IndexService()/**←要添加的metaDataValue*/, target/**←给谁添加*/);
    }
  };
}

function getParams(fn:Function){
  const ast = parseScript(fn.toString());
  let funcParams: any[] = [];
  console.log('ast树:', ast);
  /*
  Script {
  type: 'Program',
  body:
   [ FunctionDeclaration {
       type: 'FunctionDeclaration',
       id: [Identifier],
       params: [Array],
       body: [BlockStatement],
       generator: false,
       expression: false,
       async: false } ],
  sourceType: 'script' }
  * */
  const node = ast.body[0];
  if(node.type == "FunctionDeclaration"){
    funcParams = node.params;
  }
  let validParam: string[] = [];
  funcParams.forEach((obj) => {
    console.log('param:',obj); // param: Identifier { type: 'Identifier', name: 'indexService' }
    if(obj.type == "Identifier"){
      validParam.push(obj.name)
    }
  });
  return validParam;
}

function hasKey<O extends object>(obj: O, key: keyof any):key is keyof O{
  console.log('obj.hasOwnProperty(key):',obj.hasOwnProperty(key));
  return true;
}

function controller<T extends {new (...args:any[]):{}}>(constructor:T) {
  // step3
  console.log("step3", "对类进行修饰");
  class Controller extends constructor {
    // [index: string]: any;
    //step4
    constructor(...args:any[]) {
      super(args);
      console.log("step4", "真正的修饰类");
      console.log('通过ast获取函数的参数名');
      const injectParams = getParams(constructor); // [ 'indexService' ]
      let identity: string;
      for(identity of injectParams){
        if (hasKey(this, identity)/** 上面掉super(args)的时候 就已经把私有属性挂上了 故这里能查到*/) {
          this[identity] = Reflect.getMetadata(Types[identity], constructor);
        }
      }
    }
  }

  return Controller;
}

@controller
class IndexController {
  // private indexService: IIndexService;
  constructor(@inject(Types.indexService) private indexService: any) {
    // this.indexService = indexService;
  }

  info() {
    this.indexService.log('ahhh');
  }
}


// 无侵入式
const indexController = new IndexController(null);
indexController.info(); // ahhh

//todo
